package pl.vanta.utils.gui.tables;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.table.AbstractTableModel;

public abstract class VTableModel<E> extends AbstractTableModel implements IVTableModel<E>
{
	private static final long		serialVersionUID	= 5926418729329169246L;

	List<E>							items;

	protected ColumnDescription[]	columnsDescriptions;

	private ColumnDescription		indexColumnDescription;

	public VTableModel()
	{
		this.items = new ArrayList<E>();

		reloadTableStructure();
	}

	@Override
	public int getRowCount()
	{
		return this.items.size();
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex)
	{
		if (isIndex() && columnIndex == 0)
		{
			return rowIndex + 1;
		}
		else
		{
			E entity = this.items.get(rowIndex);

			return getValue(entity, calculateColumn(columnIndex));
		}
	}

	public E getItem(int index)
	{
		if (index < 0 || index >= this.items.size())
		{
			return null;
		}

		return this.items.get(index);
	}

	public List<E> getItems(int[] indicies)
	{
		List<E> subItems = new ArrayList<E>();

		for (int i = 0; i < indicies.length; i++)
		{
			E item = getItem(indicies[i]);

			if (item != null)
			{
				subItems.add(item);
			}
		}

		return subItems;
	}

	public List<E> getAllItems()
	{
		return new ArrayList<E>(this.items);
	}

	public void removeItems(int[] indices)
	{
		Arrays.sort(indices);

		for (int i = indices.length - 1; i >= 0; i--)
		{
			this.items.remove(indices[i]);
		}

		refresh();
	}

	public void clear()
	{
		this.items.clear();

		refresh();
	}

	public <T extends E> void addItem(T item)
	{
		this.items.add(item);

		refresh();
	}

	public void addItems(Collection<? extends E> items)
	{
		this.items.addAll(items);

		refresh();
	}

	public <T extends E> void addItems(T[] items)
	{
		this.items.addAll(Arrays.asList(items));

		refresh();
	}

	private void refresh()
	{
		fireTableDataChanged();
	}

	@Override
	public final int getColumnCount()
	{
		int c = this.columnsDescriptions.length;

		if (isIndex())
		{
			c++;
		}

		return c;
	}

	@Override
	public Class<?> getColumnClass(int column)
	{
		if (isIndex() && column == 0)
		{
			return Integer.class;
		}

		ColumnDescription cd = this.columnsDescriptions[calculateColumn(column)];

		return cd.getClazz() == null ? String.class : cd.getClazz();
	}

	@Override
	public final String getColumnName(int column)
	{
		if (isIndex() && column == 0)
		{
			return this.indexColumnDescription.getName();
		}

		ColumnDescription cd = this.columnsDescriptions[calculateColumn(column)];

		return cd.getName();
	}

	public int calculateColumn(int column)
	{
		if (isIndex())
		{
			return column - 1;

		}
		else
		{
			return column;
		}
	}

	public boolean isIndex()
	{
		return this.indexColumnDescription != null;
	}

	public void reloadTableStructure()
	{
		this.columnsDescriptions = getColumnsDescriptions();

		fireTableStructureChanged();
	}

	public ColumnDescription[] getColumnsDescriptions()
	{
		return this.columnsDescriptions;
	}

	public void removeAllItems()
	{
		this.items.clear();

		refresh();
	}

	public void setIndexColumnDescription(ColumnDescription indexColumnDescription)
	{
		this.indexColumnDescription = indexColumnDescription;

		reloadTableStructure();
	}

	public ColumnDescription getIndexColumnDescription()
	{
		return this.indexColumnDescription;
	}

	public void sort(Comparator<E> comparator)
	{
		Collections.sort(this.items, comparator);

		refresh();
	}
}